﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace WebLibraryMaker
{
    public static class Resources
    {
        const UInt32 RESOURCE_ID = 101;
        const UInt32 RESOURCE_TYPE = 3771;
        const UInt32 LOAD_LIBRARY_AS_DATAFILE = 2;

        [DllImport("KERNEL32.DLL", EntryPoint = "BeginUpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "UpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static bool UpdateResource(IntPtr hUpdate, UInt32 pType, UInt32 pName, UInt16 wLanguage, byte[] pData, UInt32 cbData);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "UpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static bool UpdateResource(IntPtr hUpdate, string pType, UInt32 pName, UInt16 wLanguage, byte[] pData, UInt32 cbData);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "EndUpdateResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static bool EndUpdateResource(IntPtr hUpdate, bool bDiscard);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "LoadLibraryExW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, UInt32 dwFlags);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "FreeLibrary", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static bool FreeLibrary(IntPtr hLib);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "FindResourceW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static IntPtr FindResource(IntPtr hModule, UInt32 lpID, UInt32 lpType);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "LoadResource", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
        
        [DllImport("KERNEL32.DLL", EntryPoint = "SizeofResource", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        public extern static UInt32 SizeofResource(IntPtr hModule, IntPtr hResInfo);
        
        public static byte[] LoadResources(string[] assemblies)
        {
            List<byte> bytes = new List<byte>();

            foreach (string assembly in assemblies)
            {
                IntPtr module = LoadLibraryEx(assembly, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE);
                IntPtr resource = FindResource(module, RESOURCE_ID, RESOURCE_TYPE);

                if (resource != IntPtr.Zero)
                {
                    UInt32 size = SizeofResource(module, resource);
                    IntPtr resourcePointer = LoadResource(module, resource);

                    byte[] temporaryBytes = new byte[Convert.ToInt32(size)];
                    Marshal.Copy(resourcePointer, temporaryBytes, 0, Convert.ToInt32(size));
                    bytes.AddRange(temporaryBytes);
                }

                FreeLibrary(module);
            }

            if (bytes.Count > 0)
            {
                return bytes.ToArray();
            }
            else
            {
                return null;
            }
        }

        public static void InsertResources(string path, byte[] content)
        {
            IntPtr resource = BeginUpdateResource(path, false);
            if (resource != IntPtr.Zero)
            {
                bool success = UpdateResource(resource, RESOURCE_TYPE, RESOURCE_ID, Convert.ToUInt16(1049), content, Convert.ToUInt32(content.Length));
                EndUpdateResource(resource, !success);
            }
        }
    }
}
